/*
 * archivo.c
 *
 *  Created on: 01/11/2011
 *      Author: utn_so
 */

#include "fat.h"
#include <sys/timeb.h>
#include "archivo.h"
#include "config.h"
#include "log.h"


extern struct bootsector bs;
extern struct stConfig config;
extern ARCHIVO *archivos_en_memoria;
extern pthread_mutex_t semaforoListaArchivos;

int32_t cache_archivo_crear(ARCHIVO *archivo)
{
	//LA validacion se hace en la funcion de fuse que la llama
	//Validacion: cache habilitada e archivo no existente en la lista de archivos

	int32_t i;

	//Alojo memoria para array (cant posiciones, tamanio posicion)
	archivo->cache = calloc(config.bloqpercache, sizeof(struct bloqueCache));

	//Copio el puntero para trabajar con el contenido y no perder la pos de memoria
	struct bloqueCache *bloqCache = archivo->cache;

	for(i=0;i<config.bloqpercache; i++)
	{
		bloqCache->numCluster = -1;
		bloqCache->modificado = 0;
		bloqCache->data[4096]='\0';

		bloqCache++;
	}

	if(config.loguear==1)
	{
		puts("****Se creo la Cache del Archivo****");
		//int pid = getpid();
		//logMessage();
	}

	return 1;
}

// int32_t inicializar_cache_archivo(ARCHIVO *archivo)


void cache_archivo_flush( ARCHIVO *archivo)
{
	int32_t i;
	struct bloqueCache *bCache;

	//No hay semaforo, es una funcion interna, se pone el semaforo en la funcion que la llama

	struct bloqueCache *bloqCache = archivo->cache;

	for(i=0;i<config.bloqpercache; i++)
	{
		if(bloqCache->modificado != 0)
		{
			EscribirClusterDataRegion(bloqCache->numCluster, bloqCache->data);
			bloqCache->modificado = 0;
		}

		bloqCache++;
	}

	if(config.loguear==1)
	{
		puts("****Se hizo un flush de la Cache del Archivo****");
		//int pid = getpid();
		//logMessage();
	}
}

int32_t cache_archivo_leer(ARCHIVO *archivo, int32_t numCluster, unsigned char *contenidoCluster)
{
	int32_t existe;

	//Bloqueo semaforo de cache del archivo
	pthread_mutex_lock(&(archivo->semaforoCache));

	int32_t pos = buscar_bloque(archivo, numCluster);
	if(pos != -1)
	{
		memcpy(contenidoCluster, (archivo->cache + pos)->data, *bs.secperclus * BYTESPERSEC);
		existe = 1;
		if(config.loguear==1)
		{
			char msg[70];
			sprintf(msg,"**** Se leyo la Cache del Archivo - Bloque - Cache n° %d  ****",pos);
			puts(msg);
			//int pid = getpid();
			//logMessage();
		}
	}else
	{
		existe = 0;
	}

	//Desbloqueo semaforo de cache del archivo
	pthread_mutex_unlock(&(archivo->semaforoCache));



	return existe;

}

void cache_archivo_escribir(ARCHIVO *archivo, int32_t numCluster, unsigned char *contenidoCluster)
{
	int32_t pos;
	struct bloqueCache *bCache;
	int32_t tamanioCluster = *bs.secperclus * BYTESPERSEC;

	//Bloqueo semaforo de cache del archivo
	pthread_mutex_lock(&(archivo->semaforoCache));

	pos = obtener_bloque_para_escribir(archivo, numCluster);

	bCache = (archivo->cache + pos);

	memcpy(bCache->data, contenidoCluster, tamanioCluster);
	if(bCache->numCluster == numCluster)
	{
		//Ya estaba y lo modifica
		bCache->modificado=1;
	}else
	{
		//Lo carga de disco
		bCache->modificado=0;
	}

	bCache->numCluster = numCluster;
	bCache->tiempo = tiempoEnMilisegundos();

	//Desbloqueo semaforo de cache del archivo
	pthread_mutex_unlock(&(archivo->semaforoCache));

	if(config.loguear==1)
	{
		char msg[70];
		sprintf(msg,"**** Se escribio la Cache del Archivo - Bloque - Cache n° %d  ****",pos);
		puts(msg);
		//int pid = getpid();
		//logMessage();
	}

}

int32_t buscar_bloque(ARCHIVO *archivo, int32_t numCluster)
{
	int32_t i;

	//No hay semaforo, es una funcion interna, se pone el semaforo en la funcion que la llama

	struct bloqueCache *bloqCache = archivo->cache;

	for(i=0;i<config.bloqpercache; i++)
	{
		if(bloqCache->numCluster == numCluster)
		{
			return i;
		}

		bloqCache++;
	}

	return -1;

}

int32_t obtener_bloque_para_escribir(ARCHIVO *archivo, int32_t numCluster)
{
	uint32_t LRUTime;
	int32_t posElegida;
	int32_t i;

	//No hay semaforo, es una funcion interna, se pone el semaforo en la funcion que la llama

	//Busco el bloque de numCluster, uno libre o el LRU de los no modificados
	posElegida = -1;
	LRUTime = tiempoEnMilisegundos();

	struct bloqueCache *bloqCache = archivo->cache;

	for(i = 0; i<config.bloqpercache; i++)
	{

		if(bloqCache->numCluster != -1){
			if(bloqCache->numCluster== numCluster)
			{
				posElegida = i;
				break;
			}else{
				if(bloqCache->tiempo < LRUTime && bloqCache->modificado == 0){
					LRUTime = bloqCache->tiempo;
					posElegida = i;
				}
			}
		}else{
			posElegida = i;
			break;
		}

		bloqCache++;
	}

	//Si no encontre bloque y estaban todos modificados hago un flush y devuelvo el LRU
	if (posElegida == -1)
	{
		cache_archivo_flush(archivo);
		posElegida = obtener_bloque_para_escribir(archivo, numCluster);
	}

	return posElegida;
}

void cache_archivo_cerrar(ARCHIVO *archivo)
{
	int32_t i;
	//todo validar que no haya otro archivo igual compartiendo la cache

	//No hay semaforo ya que si se esta cerrando no deberia haber otra instancia del archivo
	//Eso se valida en la funcion que llama a esta

	cache_archivo_flush(archivo);
	free (archivo->cache);

	if(config.loguear==1)
	{
		puts("**** Se cerró la Cache del Archivo  ****");
		//int pid = getpid();
		//logMessage();
	}
}

uint32_t tiempoEnMilisegundos()
{
	struct timeb currentTime;
	ftime(&currentTime);
	return (uint32_t) (1000.0 * currentTime.time + currentTime.millitm);
}



ARCHIVO *nuevoNodoArchivo()
{
	ARCHIVO *p;
    p=(ARCHIVO *)malloc (sizeof(ARCHIVO));
    if(p==NULL)
    {
        printf("Memoria RAM Llena");
        if(config.loguear == 1)
        {
            int pid = getpid();

            logMessage("nuevoNodoArchivo",pid,gettid(),3,"Memoria RAM Llena");
        }
        exit(0);
    }
    return p;
}

ARCHIVO *creaNodoArchivo()
{
	ARCHIVO *p,*lista;
	uint64_t fh = 0;
	//Creo nodo
	p=nuevoNodoArchivo();

	//Bloqueo semaforo lista archivos
	pthread_mutex_lock(&semaforoListaArchivos);

	//Busco ultimo nodo
	lista = archivos_en_memoria;
	if(lista != NULL)
	{
		fh = (*lista).fh;
		while((*lista).sig != NULL)
		{
			lista = (*lista).sig;
			fh = (*lista).fh;
		}
		(*lista).sig = p;
	}
	else
	{
		//Primer nodo de la lista
		archivos_en_memoria = p;
	}
	p->sig = NULL;
	p->fh = fh +1;
	//Inicializo semaforo de cache del archivo
	pthread_mutex_init(&(p->semaforoCache),NULL);

	//Desbloqueo semaforo lista archivos
	pthread_mutex_unlock(&semaforoListaArchivos);

	return p;
}

ARCHIVO *buscarNodoArchivoXCluster(int32_t numCluster)
{
	ARCHIVO *lista;

	//Bloqueo semaforo lista archivos
	pthread_mutex_lock(&semaforoListaArchivos);

	//Busco nodo
	lista = archivos_en_memoria;
	while(lista != NULL)
	{
		//Si encontro
		if((*lista).startcluster == numCluster)
		{
			//Desbloqueo semaforo lista archivos
			pthread_mutex_unlock(&semaforoListaArchivos);
			return lista;
		}
		//Si no
		lista = (*lista).sig;
	}

	//Desbloqueo semaforo lista archivos
	pthread_mutex_unlock(&semaforoListaArchivos);

	//No encontro devuelve 0
	return NULL;

}

ARCHIVO *buscarNodoArchivoXFH(uint64_t fh)
{
	ARCHIVO *lista;

	//Bloqueo semaforo lista archivos
	pthread_mutex_lock(&semaforoListaArchivos);

	//Busco nodo
	lista = archivos_en_memoria;
	while(lista != NULL)
	{
		//Si encontro
		if((*lista).fh == fh)
		{
			//Desbloqueo semaforo lista archivos
			pthread_mutex_unlock(&semaforoListaArchivos);
			return lista;
		}
		//Si no
		lista = (*lista).sig;
	}

	//Desbloqueo semaforo lista archivos
	pthread_mutex_unlock(&semaforoListaArchivos);

	//No encontro devuelve 0
	return NULL;

}

void *quitarNodoArchivo(ARCHIVO *arch)
{
	//Si esta abierto una sola vez cierro su cache y elimino el nodo de la lista de archivos
	//Si esta abierto mas de una vez solamente hago un flush y resto la cantidad de aperturas simultaneas
	if(arch->aperturas_simultaneas == 1)
	{
		pthread_mutex_lock(&semaforoListaArchivos);
		//Elimina archivo de la lista de archivos, tambien hace un flush de su cache y la elimina
		ARCHIVO *lista;

		//Busco nodo
		lista = archivos_en_memoria;
		if(lista->fh != arch->fh)
		{
			while((*lista).sig->fh != arch->fh)
			{
				lista = (*lista).sig;
			}
			(*lista).sig = (*arch).sig;
		}
		else
		{
			//Primer nodo de la lista
			archivos_en_memoria = (*arch).sig;
		}

		//Elimino cache
		if(config.tamanioCache > 0)
			cache_archivo_cerrar(arch);

		//Elimino nodo de la lista
		free(arch);
		pthread_mutex_unlock(&semaforoListaArchivos);
	}else
	{
		//Bloqueo semaforo de cache del archivo
		pthread_mutex_lock(&(arch->semaforoCache));

		if(config.tamanioCache > 0)
			cache_archivo_flush(arch);

		(arch->aperturas_simultaneas)--;
		//Desbloqueo semaforo de cache del archivo
		pthread_mutex_unlock(&(arch->semaforoCache));
	}

}
